using System;
using System.Collections.Generic;
using System.Linq;
using TestCases.Utils;
using TestCases.Utils.Pairs;

namespace TestCases.CompareAssert.Matchers
{
    public class NumberMatcher<T>: ObjectMatcher<T> where T: IComparable<T>
    {
        private readonly HashSet<IPair<T, T>> _ranges = new HashSet<IPair<T, T>>();

        public NumberMatcher()
        {
        }
        
        public NumberMatcher(T value) : base(value)
        {
        }

        public NumberMatcher(IEnumerable<T> values) : base(values)
        {
        }
        
        public NumberMatcher(Predicate<T> predicate) : base(predicate)
        {
        }
        
        public NumberMatcher(T min, T max)
        {
            AddRange(min, max);
        }

        public void AddRange(T min, T max)
        {
            if (max.CompareTo(min) < 0)
            {
                throw new ArgumentException("Max value must be greater or equal to min value", "max");
            }
            _ranges.Add(new Pair<T, T>(min, max));
        }

        public override void Reset()
        {
            base.Reset();
            _ranges.Clear();
        }

        public override bool IsMatch(T index)
        {
            return base.IsMatch(index) 
                || _ranges.FirstOrDefault(range => index.CompareTo(range.Left) >= 0 && index.CompareTo(range.Right) <= 0) != null;
        }

        public bool Equals(NumberMatcher<T> other)
        {
            return base.Equals(other) && _ranges.SetEquals(other._ranges);
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as NumberMatcher<T>);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode() ^ _ranges.CalculateHash();
        }
    }
}